﻿using System.Collections;
using UnityEngine;
using System.Collections.Generic;
using System.IO;
public class FM_Beam
{
    internal int index;
    internal List<FM_Note> n;
    internal FM_Score score;

    public FM_Beam(FM_Score score, int index)
    {
        n = new List<FM_Note>();
        this.index = index;
        this.score = score;
    }

    public void AddNote(FM_Note note)
    {
        n.Add(note);
    }

    public void Draw(ScoreCanvas canvas)
    {
        if (!n[0].blurred && !n[0].visible) return;

        //if (n[0].blurred)
        //    score.Font.MaskFilter = new BlurMaskFilter(15, BlurMaskFilter.Blur.Normal);
        //else
        //    score.Font.MaskFilter = null;

        float x, xe, y, ye;
        int endIndex = n.Count - 1;
        float stemLength = 2.5f;

        if (n[0].stem_up)
        {
            x = n[0].startX +
                    n[0].paddingLeft +
                    n[0].WidthAccidental() +
                    n[0].paddingNote +
                    n[0].WidthNoteNoStem();

            xe = n[endIndex].startX +
                    n[endIndex].paddingLeft +
                    n[endIndex].WidthAccidental() +
                    n[endIndex].paddingNote +
                    n[endIndex].WidthNoteNoStem();

            y = n[0].ys + (n[0].GetDisplacement() - stemLength) * score.GetDistanceBetweenStaveLines();
            ye = n[endIndex].ys + (n[endIndex].GetDisplacement() - stemLength) * score.GetDistanceBetweenStaveLines();

            if (ye > y)
            {
                float slope = FM_Const.Slope(x, y, xe, ye);
                ye = FM_Const.GetY2(slope, x, y, xe);
            }
            else
            {
                float slope = FM_Const.Slope(xe, ye, x, y);
                y = FM_Const.GetY2(slope, xe, ye, x);
            }

            float yMiddleMin = n[1].ys + (n[1].GetDisplacement() - stemLength) * score.GetDistanceBetweenStaveLines();

            for (int i = 2; i < n.Count - 1; i++)
            {
                float yMiddle = n[i].ys + (n[i].GetDisplacement() - stemLength) * score.GetDistanceBetweenStaveLines();
                if (yMiddle < yMiddleMin) yMiddleMin = yMiddle;
            }

            if ((y + ye) / 2 > yMiddleMin)
            {
                float diff = (y + ye) / 2 - yMiddleMin;
                y = y - diff;
                ye = ye - diff;
            }
        }
        else
        {
            x = n[0].startX +
                    n[0].paddingLeft +
                    n[0].WidthAccidental() +
                    n[0].paddingNote;

            xe = n[endIndex].startX +
                    n[endIndex].paddingLeft +
                    n[endIndex].WidthAccidental() +
                    n[endIndex].paddingNote;

            y = n[0].ys + (n[0].GetDisplacement() + stemLength + 1) * score.GetDistanceBetweenStaveLines();
            ye = n[endIndex].ys + (n[endIndex].GetDisplacement() + stemLength + 1) * score.GetDistanceBetweenStaveLines();

            if (ye < y)
            {
                float slope = FM_Const.Slope(x, y, xe, ye);
                ye = FM_Const.GetY2(slope, x, y, xe);
            }
            else
            {
                float slope = FM_Const.Slope(xe, ye, x, y);
                y = FM_Const.GetY2(slope, xe, ye, x);
            }

            float yMiddleMin = n[1].ys + (n[1].GetDisplacement() + stemLength + 1) * score.GetDistanceBetweenStaveLines();

            for (int i = 2; i < n.Count - 1; i++)
            {
                float yMiddle = n[i].ys + (n[i].GetDisplacement() + stemLength + 1) * score.GetDistanceBetweenStaveLines();
                if (yMiddle > yMiddleMin) yMiddleMin = yMiddle;
            }

            if ((y + ye) / 2 < yMiddleMin)
            {
                float diff = (y + ye) / 2 - yMiddleMin;
                y = y - diff;
                ye = ye - diff;
            }
        }

        // Below is the code for stems
        float slopeStems = FM_Const.Slope(x, y, xe, ye);

        //Debug.LogError($"n[0].stem_up:{n[0].stem_up}");
        if (n[0].stem_up)
        {
            for (int i = 0; i < n.Count; i++)
            {
                float tmpX = n[i].startX + n[i].paddingLeft + n[i].WidthAccidental() + n[i].paddingNote + n[i].WidthNoteNoStem();
                float tmpY = n[i].ys + n[i].GetDisplacement() * score.GetDistanceBetweenStaveLines();
                float tmpY2 = FM_Const.GetY2(slopeStems, x, y, tmpX);
                n[i].StemTopY = tmpY2;
                canvas.DrawRect(tmpX,
                                tmpY - FM_Const.dpTOpx( 1),
                                tmpX - FM_Const.dpTOpx( 1),
                                tmpY2 - score.GetDistanceBetweenStaveLines() / 2 + FM_Const.dpTOpx( 1),
                                score.Font);

                //Debug.LogError($"up tmpY:{tmpY}  tmpY2:{tmpY2}");
            }
        }
        else
        {
            for (int i = 0; i < n.Count; i++)
            {
                float tmpX = n[i].startX + n[i].paddingLeft + n[i].WidthAccidental() + n[i].paddingNote;
                float tmpY = n[i].ys + n[i].GetDisplacement() * score.GetDistanceBetweenStaveLines();
                float tmpY2 = FM_Const.GetY2(slopeStems, x, y, tmpX);
                n[i].StemTopY = tmpY2;
                canvas.DrawRect(tmpX,
                                tmpY + FM_Const.dpTOpx( 1),
                                tmpX + FM_Const.dpTOpx( 1),
                                tmpY2 - score.GetDistanceBetweenStaveLines() / 2 - FM_Const.dpTOpx(1),
                                score.Font);

                //Debug.LogError($"down tmpY:{tmpY}  tmpY2:{tmpY2}");
            }
        }

        // Below is the code for the principal beam
        GraphicPath topPath = new GraphicPath();
        if (!n[0].stem_up)
        {
            topPath.Reset();
            topPath.MoveTo(x, y - score.GetDistanceBetweenStaveLines() / 2);
            topPath.LineTo(xe + FM_Const.dpTOpx( 1), ye - score.GetDistanceBetweenStaveLines() / 2);
            topPath.LineTo(xe + FM_Const.dpTOpx(1), ye - score.GetDistanceBetweenStaveLines());
            topPath.LineTo(x, y - score.GetDistanceBetweenStaveLines());
            topPath.LineTo(x, y);
        }
        else
        {
            topPath.Reset();
            topPath.MoveTo(x - FM_Const.dpTOpx(1), y);
            topPath.LineTo(xe, ye);
            topPath.LineTo(xe, ye - score.GetDistanceBetweenStaveLines() / 2);
            topPath.LineTo(x - FM_Const.dpTOpx(1), y - score.GetDistanceBetweenStaveLines() / 2);
            topPath.LineTo(x - FM_Const.dpTOpx(1), y);

        }
        canvas.DrawPath(topPath);

        // Below is the code for beams for 1/16 note
        for (int i = 0; i < n.Count - 1; i++)
        {
            bool drawBegin = true;
            bool drawEnd = true;

            if (n[i].duration != FM_DurationValue.NOTE_SIXTEENTH && n[i].duration != FM_DurationValue.NOTE_SIXTEENTH_D)
                drawBegin = false;

            if (n[i + 1].duration != FM_DurationValue.NOTE_SIXTEENTH && n[i + 1].duration != FM_DurationValue.NOTE_SIXTEENTH_D)
                drawEnd = false;

            if (drawBegin && !drawEnd && i > 0)
                drawBegin = false;

            if (!drawBegin && drawEnd && i < n.Count - 2)
                if (n[i + 2].duration == FM_DurationValue.NOTE_SIXTEENTH || n[i + 2].duration == FM_DurationValue.NOTE_SIXTEENTH_D)
                    drawEnd = false;

            if (!drawBegin && !drawEnd)
            {
                continue;
            }

            float tmpX1 = 0;
            float tmpX2 = 0;

            if (drawBegin && drawEnd)
            {
                tmpX1 = n[i].startX + n[i].paddingLeft + n[i].WidthAccidental() + n[i].paddingNote;
                if (n[0].stem_up) tmpX1 = tmpX1 + n[i].WidthNoteNoStem() - FM_Const.dpTOpx(1);
                tmpX2 = n[i + 1].startX + n[i + 1].paddingLeft + n[i + 1].WidthAccidental() + n[i + 1].paddingNote;
                if (n[0].stem_up) tmpX2 = tmpX2 + n[i + 1].WidthNoteNoStem();
                else tmpX2 = tmpX2 + FM_Const.dpTOpx(1);
            }

            if (drawBegin && !drawEnd)
            {
                tmpX1 = n[i].startX + n[i].paddingLeft + n[i].WidthAccidental() + n[i].paddingNote;
                if (n[0].stem_up) tmpX1 = tmpX1 + n[i].WidthNoteNoStem() - FM_Const.dpTOpx(1);
                tmpX2 = tmpX1 + score.GetDistanceBetweenStaveLines() * 2 / 3;
            }

            if (!drawBegin && drawEnd)
            {
                tmpX2 = n[i + 1].startX + n[i + 1].paddingLeft + n[i + 1].WidthAccidental() + n[i + 1].paddingNote;
                if (n[0].stem_up) tmpX2 = tmpX2 + n[i + 1].WidthNoteNoStem() - FM_Const.dpTOpx(1);
                tmpX1 = tmpX2 - score.GetDistanceBetweenStaveLines() * 2 / 3;
            }

            float tmpY1 = FM_Const.GetY2(slopeStems, x, y, tmpX1);
            float tmpY2 = FM_Const.GetY2(slopeStems, x, y, tmpX2);

            if (n[0].stem_up)
            {
                tmpY1 = tmpY1 + score.GetDistanceBetweenStaveLines() * 2 / 3;
                tmpY2 = tmpY2 + score.GetDistanceBetweenStaveLines() * 2 / 3;
            }
            else
            {
                tmpY1 = tmpY1 - score.GetDistanceBetweenStaveLines() * 6 / 5;
                tmpY2 = tmpY2 - score.GetDistanceBetweenStaveLines() * 6 / 5;
            }

            GraphicPath bottomPath = new GraphicPath();
            bottomPath.Reset();
            bottomPath.MoveTo(tmpX1, tmpY1);
            bottomPath.LineTo(tmpX2, tmpY2);
            bottomPath.LineTo(tmpX2, tmpY2 - score.GetDistanceBetweenStaveLines() / 2);
            bottomPath.LineTo(tmpX1, tmpY1 - score.GetDistanceBetweenStaveLines() / 2);
            bottomPath.LineTo(tmpX1, tmpY1);

            canvas.DrawPath(bottomPath);
        }

        //score.Font.MaskFilter = null;
    }

}
